﻿using System;

using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Xml.Serialization;

namespace SQLDeploy.Data.Internal
{
    public abstract class ValidationNotifier : INotifyPropertyChanged, IValidationNotifier
    {
        public event PropertyChangedEventHandler PropertyChanged;
        
        protected internal void OnPropertyChanged(String name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(name));
        }

        private bool _hasValidated = false;
        private bool _IsValidating = false;
        private bool _IsValid = false;
        private BackgroundWorker _validateWorker = null;

        [XmlIgnore]
        public bool IsValidating
        {
            get { return _IsValidating; }
            private set
            {
                if (_IsValidating != value)
                {
                    _IsValidating = value;
                    OnPropertyChanged("IsValidating");
                }
            }
        }

        [XmlIgnore]
        public bool IsValid
        {
            get
            {
                if (!_hasValidated && !_IsValidating)
                {
                    Revalidate();
                }
                return _IsValid;
            }
            private set
            {
                if (_IsValid != value)
                {
                    _IsValid = value;
                    OnPropertyChanged("IsValid");
                }
            }
        }

        public void Begin_Revalidate()
        {
            if (IsValidating)
                throw new InvalidOperationException("Revalidate cannot be called while validation is running.");
            

            IsValidating = true;
            //onPreValidation();
            _validateWorker = new BackgroundWorker();
            _validateWorker.DoWork += _validateWorker_DoWork;
            _validateWorker.RunWorkerCompleted += _validateWorker_RunWorkerCompleted;
            _validateWorker.RunWorkerAsync();
        }

        public void End_Revalidate()
        {
            while (_validateWorker != null && _validateWorker.IsBusy)
                Thread.Sleep(500);
        }

        public void Revalidate()
        {
            IsValidating = true;
            onPreValidation();
            bool success = false;
            try
            {
                success = performValidation();
            }
            catch (Exception e)
            {
                Trace.TraceError("Validation Failure: " + e.ToString());
                success = false;
            }
            finally
            {
                onPostValidation(ref success);
                IsValid = success;

                IsValidating = false;
            }
        }

        public abstract bool performValidation();

        protected virtual void onPreValidation() { }

        protected virtual void onPostValidation(ref bool result) { }

       

        void _validateWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bool valid = false;
            if (e.Result is bool)
            {
                valid = (bool)e.Result;
            }

            onPostValidation(ref valid);
            
            
        }

        void _validateWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            bool success = false;
            try
            {
                success = performValidation();
            }
            catch (Exception ex)
            {
                Trace.TraceError("Validation Failure: " + ex.ToString());
                success = false;
            }
            finally
            {
                e.Result = success;
            }
        }
        
    }
}
